//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "battery.h"
#include "rtc.h"
#include <zenr_api.h>
#include "_sprintf.h"

//typedef struct {
//    void (*BattCapacityNotifyCallback) (unsigned char BattCapacity); //Battery capacity notify callback fn. pointer
//  void (*BattTemperatureNotifyCallback) (unsigned char BattTemperature);
//  void (*BattInvalidChargerNotifyCallback) (void);
//  void (*BattInvalidChargingCurrentNotifyCallback) (void);        //OCP
//  void (*BattInvalidHighBatteryVoltageCallback) (void);           //OVP
//  void (*ChgFinishNotifyCallback) (void);
//  unsigned char (*BattStopChargingCallback) (void);
//  unsigned char (*BattChargingErrorCallback) (void);
//  unsigned char (*BattChargingAccomplishCallback) (void);
//} BatteryCallBack;

#define VBAT_LOW			3520

EXTERN CRTC *g_pRTC;
CBattery *g_pBattery = NULL;
Nu_BatteryAPIS *g_pBatteryAPIs = NULL;
static Int32 s_beingCharge = 0;
static Int32 s_beingLowBattery = 0;
static Int32 s_LBBeenReportInInterval = 0;

//UInt32 g_isChgPlg = 0; //g_isChgPlg > 0 is plugged, g_isChgPlg <= 0 is unplugged
//UInt32 g_valtage;
//DzEvent testChargeStopEvent;

enum
{
    BATTERY_TEMPERATURE_LOW   = 0,
    BATTERY_TEMPERATURE_HIGH = 1,
    CHARGER_VOLTAGE_HIGH = 2,
    CHARGER_CURRENT_HIGH = 3,
    CI_CHARGER_TIMESUP = 4,
    CV_CHARGER_TIMESUP = 5,
    CHARGE_FINISH = 6
};


enum
{
    AC_CHARGING_PATH_ENABLED = 0,
    USB_CHARGING_PATH_ENABLED,
    NO_CHARGING_PATHS_ENABLED,
    AC_USB_CHARGING_ALLEXIST,
    AC_CHARGING_EXIST,
    USB_CHARGING_EXIST,
    NO_CHARGING_EXIST
};

enum
{
    BATT_MGR_AC_CHARGER_PLUGGED = 0,
    BATT_MGR_AC_CHARGER_UNPLUGGED = 1,
    BATT_MGR_USB_CHARGER_PLUGGED = 2,
    BATT_MGR_USB_CHARGER_UNPLUGGED = 3
};

UInt32 BatCap[11][2] = {
    {100, 4},
    {90, 4},//90%
    {80, 4},//80%
    {70, 3},//70%
    {60, 3},//60%
    {50, 2},//50%
    {40, 2},//40%
    {30, 1},//30%
    {20, 1},//20%
    {10, 0},//10%
    {0,  0},//0%
};

UInt32 percent(UInt32 percent)
{
    int i;
    for (i = 0; i < 10; i++) {
        if (percent > BatCap[i+1][0]) break;
    }
    return (BatCap[i][0] << 16) + BatCap[i][1];
}

extern "C" UInt32 AddChargeEventNodeToChargeEventQueue(EmIsrParameter *CEQueue);

void ChgStartNotifyCallback(void)
{
    //nu_puts("*************ChgStartNotifyCallback****************\n");
    s_beingCharge = 1;
    EmIsrParameter Param;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_CHARGE_START;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

//void ChgStopNotifyCallback(void)
//{
////    nu_puts("*************ChgStopNotifyCallback*****************\n");
////    EmIsrParameter Param;
////    memset(&Param,0,sizeof(Param));
////    Param.nParameter2 = PM_CHARGE_STOP;
////    AddChargeEventNodeToChargeEventQueue(&Param);
////    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
//    nu_notifyDeviceCaller((Int32)&testChargeStopEvent, SIGNALED);
//}

Uint8 StopChgNotifyCallback(void)
{
    //nu_puts("*************ChgStopNotifyCallback*****************\n");
    EmIsrParameter Param;
	s_beingCharge = 0;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_CHARGE_STOP;
    Param.nParameter3 = CHARGE_FINISH;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
    /*only for test*/
    //nu_notifyDeviceCaller((Int32)&testChargeStopEvent, SIGNALED);
    return 0;
}

void ChgUnknownNotifyCallback(void)
{
//    kprintf("ChgUnknownNotifyCallback\n");
}

void ChgFailNotifyCallback(unsigned char cause)
{
    //nu_puts("*************ChgFailNotifyCallback*****************\n");
    EmIsrParameter Param;
	s_beingCharge = 0;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_CHARGE_STOP;
    Param.nParameter3 = (Int32)cause;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

void CritVoltNotifyCallback(unsigned short vbat)
{
    //nu_puts("**************CritVoltNotifyCallback***************\n");
    EmIsrParameter Param;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_BAT_CRITICAL;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}


//void BattValNotifyCallback(BattMgrBattVal Batt_val)
//{
////    char buf[120];
////    sprintf(buf, "Vbat_max = %d\n Vbat_min = %d\n Vbat_in_mV = %d\n Vbat = %d\n Btemp = %d\n Btype = %d\n",
////    Batt_val.Vbat_max, Batt_val.Vbat_min, Batt_val.Vbat_in_mV, Batt_val.Vbat, Batt_val.Btemp, Batt_val.Btype);
////    nu_puts(buf);
////    nu_puts("****************BattValNotifyCallback****************\n");
////    g_valtage = Batt_val.Vbat_in_mV;
//    EmIsrParameter Param;
//    memset(&Param,0,sizeof(Param));
//    Param.nParameter1 = PM_BATTERY_CAP;
//    Param.nParameter3 = Batt_val.Vbat_in_mV;
//    AddChargeEventNodeToChargeEventQueue(&Param);
//    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
//}

void ChgPlgNotifyCallback(UInt8 chgsts)
{
    EmIsrParameter Param;
    memset(&Param,0,sizeof(Param));
    switch(chgsts) {
        case BATT_MGR_AC_CHARGER_PLUGGED :
            //g_isChgPlg++;
            //nu_puts("----------BATT_MGR_AC_CHARGER_PLUGGED\n");
            Param.nParameter1 = PM_CHARGE_INSERT;
            Param.nParameter2 = PM_CHARGER_WALL;
            break;
        case BATT_MGR_AC_CHARGER_UNPLUGGED :
            //g_isChgPlg--;
            //nu_puts("----------BATT_MGR_AC_CHARGER_UNPLUGGED\n");
            Param.nParameter1 = PM_CHARGE_REMOVE;
            Param.nParameter2 = PM_CHARGER_WALL;
            break;
        case BATT_MGR_USB_CHARGER_PLUGGED :
            //g_isChgPlg++;
            //nu_puts("---------BATT_MGR_USB_CHARGER_PLUGGED\n");
            Param.nParameter1 = PM_CHARGE_INSERT;
            Param.nParameter2 = PM_CHARGER_USB;
            break;
        case BATT_MGR_USB_CHARGER_UNPLUGGED :
            //g_isChgPlg--;
            //nu_puts("*********BATT_MGR_USB_CHARGER_UNPLUGGED\n");
            Param.nParameter1 = PM_CHARGE_REMOVE;
            Param.nParameter2 = PM_CHARGER_USB;
            break;
        default :
            break;
    }
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

void AsyncConvResultsNotifyCallback(BattMgrAsyncConvUpdate async_conv_results)
{
//    char buf[10];
//    sprintf(buf,"test\n");
//  nu_puts("---------AsyncConvResultsNotifyCallback---------\n");
//  kprintf("AsyncConvResultsNotifyCallback\n");
//  EmIsrParameter Param;
//  memset(&Param,0,sizeof(Param));
//  EmPostEventByIsr(212, &Param);
}

void BatteryCapacityCallback(Uint8 capacity)
{
    EmIsrParameter Param;
    memset(&Param,0,sizeof(Param));
    Param.nParameter1 = PM_BATTERY_CAP;
    Param.nParameter4 = capacity;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

void LowBatteryCallback(Uint16 vbat)
{
    EmIsrParameter Param;
    s_beingLowBattery = 1;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_BAT_LOWVOL;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

void ChgFinishNotifyCallback(Uint16 vbat_finish)
{
    EmIsrParameter Param;
    //nu_puts("***********ChgFinishNotifyCallback*************\n");
    s_beingLowBattery = 0;
    memset(&Param,0,sizeof(Param));
    Param.nParameter2 = PM_CHARGE_STOP;
    Param.nParameter3 = CHARGE_FINISH;
    AddChargeEventNodeToChargeEventQueue(&Param);
    nu_notifyDeviceCaller((Int32)&(g_pBattery->m_ChargeEvent), SIGNALED);
}

void dumpBatteryCallBack(EmIsrParameter *pParam)
{
    switch (pParam->nParameter2)
    {
        case PM_CHARGE_START:
            kprintf("--------------charge start\n");
            break;
        case PM_CHARGE_STOP:
            kprintf("--------------charge stop\n");
            break;
        case PM_BAT_LOWVOL:
            kprintf("----------------LowBatteryCallback\n");
            break;
        case PM_BAT_CRITICAL:
            kprintf("-----------------CritVoltNotifyCallback\n");
        default:
            break;
    }

    switch (pParam->nParameter1)
    {
        case PM_BATTERY_CAP:
            kprintf("-----------------BatteryCapacityCallback = %d\n", pParam->nParameter4);
        default:
            break;
    }
}

ECode CBattery::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode  CBattery::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf &buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CBattery::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf &inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;
    switch (nControlCode) {
        case 1: {
            //GetVoltage
            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < 8)  return E_INVALID_ARGUMENT;
            UInt32 valtage, capacity;
            g_pBatteryAPIs->GetBatteryChargingValtage((int *)&valtage);
            capacity = (UInt32)g_pBatteryAPIs->GetBattCapacity();
            UInt32 * p = (UInt32 *)pOutBuffer->GetPayload();
            p[0] = valtage;
            p[1] = percent(capacity);
            pOutBuffer->SetUsed(8);
            break;
        }
        case 2: {
            // Get current charge status
            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < 4) return E_INVALID_ARGUMENT;
//            UInt32 valtage;
//            g_pBatteryAPIs->GetBatteryChargingValtage((int *)&valtage);
//            if (valtage >= 4350) {
//              m_bInCharge = FALSE;
//            }
//            if (valtage < 4200 && g_isChgPlg > 0){
//              m_bInCharge = TRUE;
//            }
            Boolean bInCharge = FALSE;
            Int32 current;
            g_pBatteryAPIs->GetBatteryChargingCurrent(&current);

            if (current > 0) {
                bInCharge = TRUE;
            }

            *((Boolean *)pOutBuffer->GetPayload()) = bInCharge;
            pOutBuffer->SetUsed(sizeof(Boolean));
            break;
        }
        case 3: {
            //the current charge mode


            break;
        }
        case 4: {
            // Get the charger type -1: None, 0: AC charger 1: USB charger 2: AC & USB
            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < 4) return E_INVALID_ARGUMENT;
            Int32 mode;
            mode = g_pBatteryAPIs->GetBatteryChargingPathState();
            switch (mode) {
                case NO_CHARGING_EXIST:
                    mode = -1;
                    break;
                case USB_CHARGING_EXIST:
                    mode = 1;
                    break;
                case AC_CHARGING_EXIST:
                    mode = 0;
                    break;
                case AC_USB_CHARGING_ALLEXIST:
                    mode = 2;
                    break;
                default :
                    break;
            }
            pOutBuffer->Copy((Byte *)&mode, 4);
            pOutBuffer->SetUsed(4);
            break;
        }
        case 5: {//get temp
            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < 4) return E_INVALID_ARGUMENT;
            Int32 temp;
            g_pBatteryAPIs->GetBatteryChargingTemp(&temp);
            *((UInt32 *)pOutBuffer->GetPayload()) = temp;
            pOutBuffer->SetUsed(4);
            break;
        }
        case 6: {//get current
            if (pOutBuffer == NULL || pOutBuffer->GetCapacity() < 4) return E_INVALID_ARGUMENT;
            Int32 current;
            g_pBatteryAPIs->GetBatteryChargingCurrent(&current);
            *((UInt32 *)pOutBuffer->GetPayload()) = current;
            pOutBuffer->SetUsed(4);
            break;
        }
        case 12: { //set Charge Ok
            break;
        }
        default :
            break;
    }
    return ec;
}

//typedef struct BatteryCallback {
//    void (*ChgStartNotifyCallback) (void);
//    void (*ChgStopNotifyCallback) (void);
//    void (*ChgUnknownNotifyCallback) (void);
//    void (*ChgFailNotifyCallback) (unsigned char cause);
//    void (*CritVoltNotifyCallback) (unsigned short vbat);
//    void (*BattValNotifyCallback) (BattMgrBattVal Batt_val);
//    void (*ChgPlgNotifyCallback) (BattMgrChgSts chgsts);
//    void (*AsyncConvResultsNotifyCallback)(BattMgrAsyncConvUpdate async_conv_results);
//} BatteryCallback;

static BatteryCallback s_BatteryCallback = {
    ChgStartNotifyCallback,
    StopChgNotifyCallback,
    ChgUnknownNotifyCallback,
    ChgFailNotifyCallback,
    CritVoltNotifyCallback,
//    BattValNotifyCallback,
    ChgPlgNotifyCallback,
    AsyncConvResultsNotifyCallback,
    BatteryCapacityCallback,
    LowBatteryCallback,
    ChgFinishNotifyCallback,
};

ECode CBattery::InitChargeEventQueue()
{
    CEQueueTail = CEQueueHead = 0;
    m_reportLBTimer = NULL;
    return NOERROR;
}

extern "C" UInt32 AddChargeEventNodeToChargeEventQueue(EmIsrParameter *CEQueue)
{
    //UInt32 uFlags = SaveFlagsAndCli();
    if (g_pBattery->CEQueueHead != (g_pBattery->CEQueueTail + 1) % MAX_NUM) {
        g_pBattery->CEQueue[g_pBattery->CEQueueTail].nParameter1 = CEQueue->nParameter1;
        g_pBattery->CEQueue[g_pBattery->CEQueueTail].nParameter2 = CEQueue->nParameter2;
        g_pBattery->CEQueue[g_pBattery->CEQueueTail].nParameter3 = CEQueue->nParameter3;
        g_pBattery->CEQueue[g_pBattery->CEQueueTail].nParameter4 = CEQueue->nParameter4;
        g_pBattery->CEQueueTail = (g_pBattery->CEQueueTail + 1) % MAX_NUM ;
    }
    //RestoreIF(uFlags);
    return 0;
}

void TimerRoutineReportLB (void *)
{
    EmIsrParameter Param;
    if (s_beingLowBattery && !s_beingCharge) {
        memset(&Param,0,sizeof(Param));
        Param.nParameter2 = PM_BAT_LOWVOL;
        EmPostEventByIsr(212, &Param);
        g_pBattery->m_reportLBTimer->Restart(5 * 60 * 100);
    }
}

ECode CBattery::ChargeMgmThread()
{
    WaitResult wr;
    int valtage = 0;
    int charmode = -1;

    while (1) {
        while (CEQueueHead != CEQueueTail) {
            if (PM_BAT_LOWVOL == (UInt32)CEQueue[CEQueueHead].nParameter2) {
                if (s_LBBeenReportInInterval) {
                    CEQueueHead = (CEQueueHead + 1) % MAX_NUM;
                    continue;
                }
                s_LBBeenReportInInterval = 1;
                g_pBattery->m_reportLBTimer->Restart(5 * 60 * 100);
            }
            else if (PM_CHARGE_REMOVE == (UInt32)CEQueue[CEQueueHead].nParameter1) {
                charmode = g_pBatteryAPIs->GetBatteryChargingPathState();
                if (NO_CHARGING_EXIST == charmode) {
                    s_beingCharge = 0;
                }
                g_pBatteryAPIs->GetBatteryChargingValtage(&valtage);
                if (valtage < VBAT_LOW) {
                    g_pBattery->m_reportLBTimer->Restart(20);
                } else {
                    s_LBBeenReportInInterval = 0;
                    s_beingLowBattery = 0;
                }
            }
            else if (PM_CHARGE_STOP == (UInt32)CEQueue[CEQueueHead].nParameter2) {
                if (CHARGE_FINISH != CEQueue[CEQueueHead].nParameter3) {
                    g_pBatteryAPIs->GetBatteryChargingValtage(&valtage);
                    if (valtage < VBAT_LOW) {
                        g_pBattery->m_reportLBTimer->Restart(20);
                    } else {
                         s_LBBeenReportInInterval = 0;
                         s_beingLowBattery = 0;
                    }
                }
            }
            //dumpBatteryCallBack(&CEQueue[CEQueueHead]);
            EmPostEventByIsr(212, &CEQueue[CEQueueHead]);
            CEQueueHead = (CEQueueHead + 1) % MAX_NUM;
        }
        m_ChargeEvent.Wait(&wr, NULL);
    }
    return NOERROR;
}

ECode ChargeThreadRoutine(void *arg)
{
    g_pBattery->ChargeMgmThread();
    return NOERROR;
}

ECode CreateChargeThread()
{
    ECode ec;
    ec = DzCreateKernelThread(ChargeThreadRoutine,
        (PVoid)NULL, CreateThreadFlag_System, ThreadPriorityRank_Normal, NULL);
    if (FAILED(ec)) {
        BAT_DBG( cprintf("unable to create ctrl thread ec : %08x\n", ec) );
        return ec;
    }
    BAT_DBG( cprintf("Create Charge Management Thread Succeeded!\n") );
    return ec;
}


//ECode TestChargeStopThreadRoutine(void *)
//{
//    WaitResult wr;
//    kprintf("\n\n\n\n\ntest charge stop Thread Routine prepare to sleep\n\n\n\n\n");
//    while (1) {
//        testChargeStopEvent.Wait(&wr, NULL);
//        kprintf("***************************************\n" );

//    }
//    return NOERROR;
//}

//ECode CreateTestChargeStopThread()
//{
//    ECode ec;
//    ec = DzCreateKernelThread(TestChargeStopThreadRoutine,
//        (PVoid)NULL, CreateThreadFlag_System, ThreadPriorityRank_Normal, NULL);
//    if (FAILED(ec)) {
//        BAT_DBG( cprintf("unable to create ctrl thread ec : %08x\n", ec) );
//        return ec;
//    }
//    BAT_DBG( cprintf("Create Charge Management Thread Succeeded!\n") );
//    return ec;
//}

EXTERN IDeviceDriver * CDECL CreateBattery(uint_t uDeviceNo, void *pvParameter)
{
    kprintf("CreateBattery\n");

    g_pBattery = new CBattery;
    if (!g_pBattery) {
        kprintf("not enough memory\n");
        return NULL;
    }
    g_pBattery->InitChargeEventQueue();

    g_pBatteryAPIs = nu_get_BatteryAPIs();
    g_pBattery->m_reportLBTimer = new DzTimer(5 * 60 * 100, TimerRoutineReportLB, NULL);
    //g_pBatteryAPIs->RegisterBatteryCallback();
	g_pBatteryAPIs->RegisterBatteryCallback(&s_BatteryCallback);
    CreateChargeThread();
    //CreateTestChargeStopThread();

    if (NULL == g_pBatteryAPIs) {
        kprintf("Init Battery Failed!\n");
        return NULL;
    }

    return g_pBattery;
}

